package ar.uba.fi.tp2Unit.framework;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import ar.uba.fi.tp2Unit.assertions.AssertionFailException;

/**
 * It represents a test method to be run by a {@link TestRunner}. 
 * It also can be grouped by e {@link TestSuite} in order to be run
 * inside of it and sharing its {@link TestFixture}.
 * It passes its result to {@link TestResult}
 * 
 * @author grupo12
 * @see {@link TestSuite}, {@link Test}, {@link TestResult}, {@link TestRunner}
 */
public abstract class TestCase extends Test {

	private static final String SKIP_TEST_TAG = "SKIP";
	private String name;
	private List<String> tags;	

	public TestCase(String name) {
		this.name = name;
		this.tags = new ArrayList<String>();
	}
	
	public TestCase(String name, String tag) {
		this.name = name;
		this.tags = new ArrayList<String>();
		this.tags.add(tag);
	}
	
	public TestCase(String name, List<String> tags) {
		this.name = name;
		this.tags = new ArrayList<String>(tags);
	}	

	@Override
	public void test(TestResult testResult, String pattern, List<String> runTags, TestFixture testFixture) {
		long startTime = 0;
		String elapsedTime;
		
		if(matchesPattern(pattern) && matchesTags(runTags)){
			setUp(testFixture);
			try {
				startTime = System.currentTimeMillis();
				testMethod(testFixture);
				elapsedTime = getElapsedTime(startTime);
				testResult.addSuccessful(getName(), getParentSuiteName(), "", elapsedTime);
			} catch (AssertionFailException e) {
				elapsedTime = getElapsedTime(startTime);
				testResult.addFail(getName(), getParentSuiteName(), e.getMessage(), elapsedTime);
			} catch (Throwable e) {
				elapsedTime = getElapsedTime(startTime);
				testResult.addError(getName(), getParentSuiteName(), e.getMessage(), elapsedTime);
			} finally {
				tearDown(testFixture);
			}
		}
	}

	private String getElapsedTime(long startTime) {
		long endTime = System.currentTimeMillis();
		long elapsedTime = endTime - startTime;
		Date elapsedDate = new Date(elapsedTime);
		String formattedElapsedDate = new SimpleDateFormat("s.SSS")
				.format(elapsedDate);
		return formattedElapsedDate;
	}		

	private String getParentSuiteName() {
		if(getTestSuite()!=null){
			return getTestSuite().getSuiteName();			
		}
		return "";
	}

	public abstract void testMethod(TestFixture testFixture) throws Throwable;

	public void addTag(String tag){
		tags.add(tag);
	}
	
	private boolean matchesTags(List<String> runTags) {
		if(tags.contains(SKIP_TEST_TAG)){
			return false;
		}
		if(runTags.isEmpty()){
			return true;
		}
        return containsAnyOfTheRunTags(runTags);		
	}

	private boolean containsAnyOfTheRunTags(List<String> runTags) {
		for (String actualTag : runTags) {
			if(tags.contains(actualTag)) {
                return true;
            }
        }               
		return false;
	}	
	
	@Override
	public String getName() {
		return name;
	}
}
